package juegocartas.mus;

import java.util.ArrayList;
import java.util.List;

/**
 * Mesa de mus. Es donde se produce el juego. Cada cierto tiempo
 * @author Jorge Berjano
 */
public class MesaMus  {

    private final MotorMus motorMus = new MotorMus(this);
    private final List<ObservadorMesaMus> observadores = new ArrayList<ObservadorMesaMus>();
    private final List<FaseLance> lancesPendientes = new ArrayList<FaseLance>();
    private final List<FaseLance> lancesJugados = new ArrayList<FaseLance>();
    private List<JugadorMus> listaJugadores;
    private GrupoCartas mazo;
    private final FaseMus faseMus = new FaseMus(this);
    private FaseLance lanceActual;
    private Fase faseActual;
    private int numeroPuntosParaGanar = 30;
    
    private Equipo equipoGanador;

    public MesaMus() {
        mazo = GrupoCartas.crearBarajaEspanola();
    }
    
    /**
     * Procesa la mesa. Debe ser llamado cada cierto tiempo para mantener
     * la actividad del juego.
     */
    public void procesar() {       
        motorMus.procesar();
    }

    /**
     * Agrega un observador a la mesa.
     */
    public void agregarObservador(ObservadorMesaMus observador) {
        observadores.add(observador);
    }

    /**
     * Quita un observador a la mesa.
     */
    public void quitarObservador(ObservadorMesaMus observador) {
        observadores.remove(observador);
    }

    /**
     * Obtiene los observadores de la mesa.
     */
    public List<ObservadorMesaMus> getObservadoresMesa() {
        return observadores;
    }
    
    public Fase getFase() {
        return faseActual;
    }

    /**
     * Inicia una partida.
     */
    public void iniciarPartida(List<JugadorMus> listaJugadores) {
        this.listaJugadores = listaJugadores;
         for (JugadorMus jugador : listaJugadores) {            
            jugador.setObservador(motorMus);
            agregarObservador(jugador);
        }
        iniciarMano();
    }

    /**
     * Inicia una mano.
     */
    private void iniciarMano()
    {
        recojerCartas();

        for (ObservadorMesaMus observador : getObservadoresMesa()) {
            observador.informarInicioMano();
        }
        
        barajar();
        repartirCartas();

        iniciarFase(faseMus);
    }

    protected void recojerCartas() {
        for (JugadorMus jugador : listaJugadores) {
            mazo.añadirCartas(jugador.devolverCartas());
        }
    }

    /**
     * Baraja las cartas del mazo.
     */
    public void barajar() {
        mazo.barajar();
    }
    
    private void devolverCartas() {
        for (JugadorMus jugador : listaJugadores) {
            agregarAlMazo(jugador.devolverCartas());
        }
    }

   /**
     * Reparte las cartas a los jugadores.
     */
    public void repartirCartas() {
        for (int i = 0; i < 4; i++) {
            for (JugadorMus jugador : listaJugadores) {
                repartirCartas(jugador, 1);
            }
        }
    }

    /**
     * Reparte un numero de cartas a un jugador. Sirve para reponer cartas tras
     * un descarte.
     */
    public void repartirCartas(JugadorMus jugador, int numeroCartas) {
        for (int i = 0; i < numeroCartas; i++) {
            Carta carta = mazo.cogerCartaSuperior();
            if (carta == null) {
                System.out.println("No quedan cartas en el mazo");
            } else {
                jugador.cojerCarta(carta);
            }
        }
    }
    
    private void iniciarFase(Fase fase) {
        faseActual = fase;
        for (ObservadorMesaMus observador : getObservadoresMesa()) {
            observador.informarInicioFase(fase);
        }
        fase.iniciar();
    }

    /**
     * Indica que se ha finalizado la mano.
     */            
    public void finalizarMano() {
        
        mostrarCartas();
        
        contabilizarPuntos();
        
        if (equipoGanador != null) {
            informarVictoria(equipoGanador);
        } else {
            devolverCartas();
            
            iniciarMano();
        }
    }
    
    private boolean comprobarSiHayGanador() {
        for (JugadorMus jugador : listaJugadores) {
            if (jugador.getEquipo().getPuntos() >= numeroPuntosParaGanar) {
                equipoGanador = jugador.getEquipo();
                return true;
            }
        }
        return false;
    }
    
    public void informarVictoria(Equipo equipo) {    
            for (ObservadorMesaMus observador : getObservadoresMesa()) {
                observador.informarVictoria(equipo);
            }
        }
    
    
    private void mostrarCartas() {
         for (ObservadorMesaMus observador : getObservadoresMesa()) {
            for (JugadorMus jugador : listaJugadores) {
                observador.mostrarCartas(jugador);
            }
        }
    }
    
    /**
     * Contabiliza los puntos de los lances segun las cartas de cada jugador.
     */
    private void contabilizarPuntos() {
        for (FaseLance lance : lancesJugados) {
            lance.contabilizarPuntos();
            if (comprobarSiHayGanador()) {
                return;
            }                
        }
        
        iniciarMano();
    }
    
    /**
     * Inicia las fases de lances.
     */
    public void iniciarLances() {
        lanceActual = null;
        lancesPendientes.add(new FaseLance(this, new LanceGrandes()));
        lancesPendientes.add(new FaseLance(this, new LanceChicas()));
        lancesPendientes.add(new FaseLance(this, new LancePares()));
        lancesPendientes.add(new FaseLance(this, new LanceJuego()));        
        iniciarSiguienteLance();
    }

    /**
     * Se llama desde una fase de lance cuando ningun jugador tiene con cartas
     * para jugar el lance.
     */
    public void nadieTieneCartasParaJugarLance() {
        if (lancesPendientes.isEmpty()) {
            lanceActual = new FaseLance(this, new LancePunto());
            iniciarFase(lanceActual);
        } else {
            iniciarSiguienteLance();
        }
    }

    /**
     * Inicia el siguiente lance.
     */
    public void iniciarSiguienteLance() {

        if (lanceActual != null) {
            lancesJugados.add(lanceActual);
        }

        lanceActual = null;
        if (lancesPendientes.isEmpty()) {
            finalizarMano();            
            return;
        }
        lanceActual = lancesPendientes.get(0);
        lancesPendientes.remove(0);

        iniciarFase(lanceActual);
    }

    /**
     * Obtiene todos los jugadores.
     */
    public List<JugadorMus> getJugadores() {
        return listaJugadores;
    }

    /**
     * Obtiene los jugadores excepro el indicado.
     */
    public List<JugadorMus> getOtrosJugadores(JugadorMus jugador) {
        List<JugadorMus> otrosJugadores = new ArrayList<JugadorMus>();
        otrosJugadores.addAll(listaJugadores);
        otrosJugadores.remove(jugador);
        return otrosJugadores;
    }

    /**
     * Obtiene el primer jugador, o sea el que es mano.
     * @return 
     */
    public JugadorMus getPrimerJugador() {
        return listaJugadores.get(0);
    }

    /**
     * Obtiene el siguiente jugador a uno dado.
     */
    public JugadorMus getSiguienteJugador(JugadorMus jugador) {
        int indice = listaJugadores.indexOf(jugador) + 1;
        if (indice < listaJugadores.size()) {
            return listaJugadores.get(indice);
        } else {
            return null;
        }
    }

    /**
     * Agrega al mazo una serie de cartas.
     */
    public void agregarAlMazo(List<Carta> cartas) {
        mazo.añadirCartas(cartas);
    }

    public List<JugadorMus> getJugadoresContrarios(JugadorMus jugador) {
        return jugador.getEquipo().getJugadores();
    }

    public void error(String mensaje) {
        for (ObservadorMesaMus observador : getObservadoresMesa()) {
            observador.informarError(mensaje);
        }
    }
    
    public void error(JugadorMus jugador, String mensaje) {
        String texto = "Error de " + jugador + ": " + mensaje;
        for (ObservadorMesaMus observador : getObservadoresMesa()) {
            observador.informarError(texto);
        }
    }
}
